// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
/**
 * @File Name: contest_guide.cpp
 * @Brief :
 * @Author : Yell (12112088@qq.com)
 * @Version : 1.0
 * @Creat Date : 2023-09-15
 *
 */
#pragma once
#include <set>
#include <string>
#include <vector>
#include <cfloat>
 
#include "contest_pin.h"
#include "Rectangle.hh"
#include "Point.hh"
#include "contest_coord.h"

namespace ieda_contest {

enum class ContestInstanceType { kNoInstanceType, kLogic, kFlipFlop, kBuffer, kMacro, kBoundaryCell };
enum class ContestInstanceState { kNoInstanceState, kFixed, kPlaceable, kPlaced };


class ContestInstance
{


 public:
  ContestInstance() = default;
  explicit ContestInstance(std::string name);

  ~ContestInstance() = default;
  // getter
  std::string& get_name() { return _name; }
  ContestCoord& get_real_coord() { return _real_coord; }
  ContestCoord& get_grid_coord() { return _grid_coord; }
  int get_area() const { return _area; }
  int32_t get_width() const { return _width; }
  int32_t get_height() const { return _height; }
  Point<int32_t> get_lower_point() const { return _lower_point; }
  Point<int32_t> get_upper_point() const { return _upper_point; }
  Point<int32_t> get_center_coord() const;
  Point<int32_t> get_anchor_center_point() const { return _anchor_center_point; }

  std::vector<ContestPin*> get_pin_vec() const { return _pins; }
  std::vector<ContestPin*> get_outpin_vec() const;//PI和clock pin
  std::vector<ContestPin*> get_inpin_vec() const;//PO和FFclk pin

  Point<float> get_instance_early_late_delay() const;
  Point<float> get_outpin_delay(ContestPin* outpin) const;//给定一个outpin得到instance的delay
  bool get_marked() const { return _marked; }
  double get_hpwl() const { return _hpwl; }
  Point<int32_t> get_initial_pos() const { return _initial_pos;}


  bool isLogic() { return _instance_type == ContestInstanceType::kLogic; }
  bool isFlipflop() { return _instance_type == ContestInstanceType::kFlipFlop; }
  bool isBuffer() { return _instance_type ==ContestInstanceType::kBuffer; }
  bool isMacro() { return _instance_type == ContestInstanceType::kMacro; }
  bool isBoundaryCell() { return _instance_type == ContestInstanceType::kBoundaryCell; }

  bool isFixed() { return _instance_state == ContestInstanceState::kFixed; }
  bool isPlaceable() { return _instance_state == ContestInstanceState::kPlaceable; }
  bool isPlaced() { return _instance_state == ContestInstanceState::kPlaced; }

  // setter
  void set_name(const std::string& name) { _name = name; }
  void set_real_coord(const ContestCoord& real_coord) { _real_coord = real_coord; }
  void set_grid_coord(const ContestCoord& grid_coord) { _grid_coord = grid_coord; }
  void set_area(const int area) { _area = area; }

  void set_state(ContestInstanceState state) { _instance_state = state; }
  void set_type(ContestInstanceType type) { _instance_type = type; }
  void set_lower_point(Point<int32_t> point) { _lower_point = point; }
  void set_upper_point(Point<int32_t> point) { _upper_point = point; }
  void set_center_point(Point<int32_t> point);
  void set_anchor_center_point(Point<int32_t> point) { _anchor_center_point = point; }
  void set_width(int32_t width) { _width = width; }
  void set_height(int32_t height) { _height = height; }
  void set_marked(bool  marked){ _marked = marked; }
  void set_hpwl(double wl) { _hpwl = wl; }
  void add_pin(ContestPin* pin) { _pins.push_back(pin); }
  void set_initial_pos(Point<int32_t> pos_before_opt){ _initial_pos = pos_before_opt;}

  void uppdate_coord(Point<int32_t> point);//更新自身coord，和对应的pin的coord。

 private:
  std::string _name;
  ContestCoord _real_coord;
  ContestCoord _grid_coord;
  int _area;

  ContestInstanceState _instance_state;
  ContestInstanceType _instance_type;
  
  std::vector<ContestPin*> _pins;

  int32_t _width;
  int32_t _height;

  Point<int32_t> _lower_point;
  Point<int32_t> _upper_point;
  Point<int32_t> _anchor_center_point;
  Point<int32_t> _initial_pos; //用于每个算法开始暂存cell location。

  bool _marked;
  double _hpwl;
};
inline ContestInstance::ContestInstance(std::string name) : _name(std::move(name)),_instance_state(ContestInstanceState::kNoInstanceState), _instance_type(ContestInstanceType::kNoInstanceType)
{
}

inline std::vector<ContestPin*> ContestInstance::get_outpin_vec() const {
    std::vector<ContestPin*> out_pins;
    for (auto pin : _pins) {

        if (pin->isInstanceOutputPin() || pin->isPI() || pin->isClockPin()) {
            out_pins.push_back(pin);
        }
    }
    return out_pins;
}

inline std::vector<ContestPin*> ContestInstance::get_inpin_vec() const {
    std::vector<ContestPin*> in_pins;
    for (auto pin : _pins) {
      //TODO:为什么po是inpin？？？
        if (pin->isInstanceInputPin() || pin->isPO() || pin->isFlipFlopClkPin()) {
            in_pins.push_back(pin);
        }
    }
    return in_pins;
}

inline void ContestInstance::uppdate_coord(Point<int32_t> point) {
    int32_t inst_width = this->get_width();
    int32_t inst_height = this->get_height();

    _lower_point = point;
    _upper_point = Point<int32_t>(point.get_x() + inst_width, point.get_y() + inst_height);

    for (auto pin : this->get_pin_vec()) {
        int32_t pin_center_x = _lower_point.get_x() + inst_width / 2 + pin->get_offset_center_coord().get_x();
        int32_t pin_center_y = _lower_point.get_y() + inst_height / 2 + pin->get_offset_center_coord().get_y();

        // int32_t pin_center_x = _lower_point.get_x() + pin->get_offset_center_coord().get_x();
        // int32_t pin_center_y = _lower_point.get_y() + pin->get_offset_center_coord().get_y();

        pin->set_coord(Point<int32_t>(pin_center_x, pin_center_y));
    }
}

inline void ContestInstance::set_center_point(Point<int32_t> point) {
    int32_t lower_x = point.get_x() - (this->get_width() / 2);
    int32_t lower_y = point.get_y() - (this->get_height() / 2);

    this->uppdate_coord(Point<int32_t>(lower_x, lower_y));
}

inline Point<int32_t> ContestInstance::get_center_coord() const {
    int32_t center_x = _lower_point.get_x() + _width / 2;
    int32_t center_y = _lower_point.get_y() + _height / 2;
    return Point<int32_t>(center_x, center_y);
}

inline Point<float> ContestInstance::get_instance_early_late_delay() const {
    double out_min_early_at = DBL_MAX;
    double in_min_early_at = DBL_MAX;
    double out_max_late_at = DBL_MIN;
    double in_max_late_at = DBL_MIN;

    if (_instance_type == ContestInstanceType::kFlipFlop) {
        ContestPin* ck_port = nullptr;
        for (auto pin : _pins) {
            if (pin->isFlipFlopClkPin()) {
                ck_port = pin;
            }
        }
        if (this->get_outpin_vec().empty()) {
            return Point<float>(0.0, 0.0);
        }

        in_min_early_at = ck_port->get_arrival_early_time();
        in_max_late_at = ck_port->get_arrival_late_time();

        for (auto outpin : this->get_outpin_vec()) {
            double out_early_at = outpin->get_arrival_early_time();
            out_early_at < out_min_early_at ? out_min_early_at = out_early_at : out_min_early_at;

            double out_late_at = outpin->get_arrival_late_time();
            out_late_at > out_max_late_at ? out_max_late_at = out_late_at : out_max_late_at;
        }

    } else {
        auto outpins = get_outpin_vec();
        if (outpins.size() == 0) {
            return Point<float>(0.0, 0.0);
        }

        for (auto outpin : outpins) {
            double out_early_at = outpin->get_arrival_early_time();
            out_early_at < out_min_early_at ? out_min_early_at = out_early_at : out_min_early_at;

            double out_late_at = outpin->get_arrival_late_time();
            out_late_at > out_max_late_at ? out_max_late_at = out_late_at : out_max_late_at;
        }

        auto inpins = get_inpin_vec();
        for (auto inpin : inpins) {
            double in_early_at = inpin->get_arrival_early_time();
            in_early_at < in_min_early_at ? in_min_early_at = in_early_at : in_min_early_at;

            double in_late_at = inpin->get_arrival_late_time();
            in_late_at > in_max_late_at ? in_max_late_at = in_late_at : in_max_late_at;
        }
    }

    double min_delay = out_min_early_at - in_min_early_at;
    double max_delay = out_max_late_at - in_max_late_at;

    return Point<float>(min_delay, max_delay);
}

inline Point<float> ContestInstance::get_outpin_delay(ContestPin* outpin) const {
    double outpin_min_at = outpin->get_arrival_early_time();
    double outpin_max_at = outpin->get_arrival_late_time();
    double inpin_min_at = DBL_MAX;//TODO：待办
    double inpin_max_at = DBL_MIN;

    if (_instance_type == ContestInstanceType::kFlipFlop) {
        ContestPin* ck_port = nullptr;
        for (auto pin : _pins) {
            if (pin->isFlipFlopClkPin()) {
                ck_port = pin;
            }
        }

        inpin_min_at = ck_port->get_arrival_early_time();
        inpin_max_at = ck_port->get_arrival_late_time();
    } else {
        auto inpins = get_inpin_vec();
        for (auto inpin : inpins) {
            double in_early_at = inpin->get_arrival_early_time();
            in_early_at < inpin_min_at ? inpin_min_at = in_early_at : inpin_min_at;

            double in_late_at = inpin->get_arrival_late_time();
            in_late_at > inpin_max_at ? inpin_max_at = in_late_at : inpin_max_at;
        }
    }

    double min_delay = outpin_min_at - inpin_min_at;
    double max_delay = outpin_max_at - inpin_max_at;

    return Point<float>(min_delay, max_delay);
}


}  // namespace ieda_contest
